package com.example.johnerez.myapplication;

import android.accounts.AccountsException;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.support.v4.util.LruCache;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.RatingBar;
import android.widget.TextView;

import com.android.volley.RequestQueue;
import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.NetworkImageView;
import com.android.volley.toolbox.Volley;

import org.json.JSONException;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Created by johnerez on 1/6/15.
 */
public class DishAdapter extends BaseAdapter {
    private static final String DEBUG_TAG = "DEBUG_TAG DishAdapter";
    private static final DecimalFormat PRICE_DECIMAL_FORMAT = new DecimalFormat("$#.00");
    private static final DecimalFormat MILES_DECIMAL_FORMAT = new DecimalFormat("#.0");
    private final Context context;
    private final LayoutInflater layoutInflater;
    private List<DishTile> shownDishes;
    private List<DishTile> dishes;
    private RequestQueue mRequestQueue;
    private ImageLoader mImageLoader;
    private HashMap<NetworkImageView, String> lastUrlForImageView = new HashMap<>();
    private static HashMap<String, String> imageUrlForFetchUrl = new HashMap<>();


    public DishAdapter(Context c) {
        context = c;
        layoutInflater = LayoutInflater.from(context);

        mRequestQueue = Volley.newRequestQueue(context);
        mImageLoader = new ImageLoader(mRequestQueue, new ImageLoader.ImageCache() {
            private final LruCache<String, Bitmap> mCache = new LruCache<String, Bitmap>(10);

            public void putBitmap(String url, Bitmap bitmap) {
                mCache.put(url, bitmap);
            }

            public Bitmap getBitmap(String url) {
                return mCache.get(url);
            }
        });
    }

    public List<DishTile> getDishes() {
        return dishes;
    }

    public void setDishes(List<DishTile> dishes) {
        this.dishes = dishes;
        this.shownDishes = dishes;
        this.notifyDataSetInvalidated();

        startFillingImageUrlCache(dishes);
    }

    private void startFillingImageUrlCache(List<DishTile> dishes) {
        return;
        /*
        for (DishTile dish : dishes) {
            String imageUrlFetchUrl = "http://mac-and-cheese.sandbox.google.com/image?query=";
            try {
                imageUrlFetchUrl += canonicalizeAndUrlEncode(dish.dish.name);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            if (!imageUrlForFetchUrl.containsKey(imageUrlFetchUrl)) {
                Log.d(DEBUG_TAG, "Fetching image URL from " + imageUrlFetchUrl);
                new DownloadWebpageTaskForImage(null, mImageLoader, imageUrlFetchUrl).execute();
            }
        }
        */
    }

    public void setShownDishes(List<DishTile> dishes) {
        this.shownDishes = dishes;
    }

    @Override
    public int getCount() {
        return shownDishes != null ? shownDishes.size() : 0;
    }

    @Override
    public DishTile getItem(int position) {
        return shownDishes.get(position);
    }

    @Override
    public long getItemId(int position) {
        return 0;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        Log.d(DEBUG_TAG, "getView(" + position + ")");
        View view;
        if (convertView == null) { // Create a new view if no recycled view is available
            view = (View) layoutInflater.inflate(
                    R.layout.list_view_item, parent, false /* attachToRoot */);
        } else {
            view = (View) convertView;
        }

        DishTile tileData = (DishTile) getItem(position);

        TextView restaurantNameView = (TextView) view.findViewById(R.id.restaurantName);
        TextView dishNameView = (TextView) view.findViewById(R.id.dishName);
        TextView priceView = (TextView) view.findViewById(R.id.dishPrice);
        RatingBar ratingBar = (RatingBar) view.findViewById(R.id.dishStarRating);
        TextView dishDescriptionView = (TextView) view.findViewById(R.id.dishDescription);
        TextView restaurantDistance = (TextView) view.findViewById(R.id.restaurantDistance);
        NetworkImageView imageView = (NetworkImageView) view.findViewById(R.id.imageView);

        restaurantNameView.setText(tileData.restaurant.name);
        dishNameView.setText(tileData.dish.name);
        priceView.setText(PRICE_DECIMAL_FORMAT.format(tileData.dish.price));
        ratingBar.setRating(tileData.restaurant.yelpRating);
        dishDescriptionView.setText(tileData.dish.description);
        restaurantDistance.setText(formatDistance(tileData.restaurant.distanceKm));

        //imageView.setImageUrl(tileData.dish.imageUrl, mImageLoader);
        String imageUrlFetchUrl = "http://mac-and-cheese.sandbox.google.com/image?query=";
        try {
            imageUrlFetchUrl += canonicalizeAndUrlEncode(tileData.dish.name);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        Log.d(DEBUG_TAG, "imageUrlFetchUrl: " + imageUrlFetchUrl);
        if (imageUrlForFetchUrl.containsKey(imageUrlFetchUrl)) {
            Log.d(DEBUG_TAG, "imageUrlFetchUrl in cache. Result: " + imageUrlForFetchUrl.get(imageUrlFetchUrl));
            imageView.setImageUrl(imageUrlForFetchUrl.get(imageUrlFetchUrl), mImageLoader);
        } else {
            Log.d(DEBUG_TAG, "imageUrlFetchUrl not in cache. Fetching image URL for dish...");
            imageView.setImageUrl("", mImageLoader);
            this.lastUrlForImageView.put(imageView, imageUrlFetchUrl);
            new DownloadWebpageTaskForImage(imageView, mImageLoader, imageUrlFetchUrl).execute();
        }


        return view;
    }

    private String canonicalizeAndUrlEncode(String name) throws UnsupportedEncodingException {
        name = name.toLowerCase();
        String alpha = name.replaceAll("[^a-zA-Z\\s]", "").replaceAll("\\s+", " ");
        return URLEncoder.encode(alpha, "UTF-8");
    }

    // Uses AsyncTask to create a task away from the main UI thread. This task takes a
    // URL string and uses it to create an HttpUrlConnection. Once the connection
    // has been established, the AsyncTask downloads the contents of the webpage as
    // an InputStream. Finally, the InputStream is converted into a string, which is
    // displayed in the UI by the AsyncTask's onPostExecute method.
    private class DownloadWebpageTaskForImage extends AsyncTask<String, Void, String> {
        private final String fetchUrl;
        private final boolean onlyUpdateImageUrlCache;
        private NetworkImageView imageView;
        private ImageLoader loader;

        public DownloadWebpageTaskForImage(NetworkImageView imageView, ImageLoader loader, String fetchUrl) {
            this.imageView = imageView;
            this.loader = loader;
            this.fetchUrl = fetchUrl;
            this.onlyUpdateImageUrlCache = imageView == null;
        }

        @Override
        protected String doInBackground(String... urls) {
            if (!onlyUpdateImageUrlCache && !fetchUrl.equals(lastUrlForImageView.get(imageView))) {
                return null;
            }
            if (imageUrlForFetchUrl.containsKey(fetchUrl)) {
                return imageUrlForFetchUrl.get(fetchUrl);
            }
            // params comes from the execute() call: params[0] is the url.
            try {
                return downloadUrl(fetchUrl);
            } catch (IOException e) {
                e.printStackTrace();
            } catch (AccountsException e) {
                e.printStackTrace();
            } catch (JSONException e) {
                e.printStackTrace();
            }
            return null;
        }

        // onPostExecute displays the results of the AsyncTask.
        @Override
        protected void onPostExecute(String result) {
            Log.d(DEBUG_TAG, "Got image URL for fetchUrl: " + fetchUrl);
            Log.d(DEBUG_TAG, "Image URL: " + result);
            imageUrlForFetchUrl.put(fetchUrl, result);
            if (!onlyUpdateImageUrlCache) {
                if (!fetchUrl.equals(lastUrlForImageView.get(imageView))) {
                    return;
                }
                imageView.setImageUrl(result, loader);
            }
        }

        // Given a URL, establishes an HttpUrlConnection and retrieves
        // the web page content as a InputStream, which it returns as
        // a string.
        private String downloadUrl(String myUrl) throws IOException, AccountsException, JSONException {
            InputStream is = null;

            try {
                URL url = new URL(myUrl);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setReadTimeout(10000 /* milliseconds */);
                conn.setConnectTimeout(15000 /* milliseconds */);
                conn.setRequestMethod("GET");
                conn.setDoInput(true);
                // Starts the query
                conn.connect();
                int response = conn.getResponseCode();
                is = conn.getInputStream();

                return inputStreamToString(is);

                // Makes sure that the InputStream is closed after the app is
                // finished using it.
            } finally {
                if (is != null) {
                    is.close();
                }
            }
        }

        private String inputStreamToString(InputStream stream) throws IOException {
            BufferedReader reader = new BufferedReader(
                    new InputStreamReader(stream, "UTF-8"));
            StringBuilder result = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                result.append(line);
            }
            return result.toString();
        }

    }

    private String formatDistance(double distanceKm) {
        Log.d(DEBUG_TAG, "formatDistance: distanceKm * 0.62137 = " + distanceKm * 0.62137);
        return MILES_DECIMAL_FORMAT.format(distanceKm * 0.62137) + " miles";
    }
}
